iT邦幫忙

2024 iThome 鐵人賽

DAY 6
0

Day23 要做的是將輸入的文字轉為聲音

資料

const MESSAGE = "Hello!";

  const [text, setText] = useState(MESSAGE);
  const synthRef = useRef(window.speechSynthesis);
  const utteranceRef = useRef(new SpeechSynthesisUtterance());

開始與停止說話

  const speak = useCallback(() => {
    const synth = synthRef.current;
    const utterance = utteranceRef.current;

    if (synth.speaking) {
      synth.cancel();
      return;
    }

    utterance.text = text;
    synth.speak(utterance);
  }, [text]);

  const stop = useCallback(() => {
    synthRef.current.cancel();
  }, []);

更新狀態

  const updateVoice = useCallback((index: number) => {
    const voices = synthRef.current.getVoices();
    utteranceRef.current.voice = voices[index];
  }, []);

  const updateRate = useCallback((rate: number) => {
    utteranceRef.current.rate = rate;
  }, []);

  const updatePitch = useCallback((pitch: number) => {
    utteranceRef.current.pitch = pitch;
  }, []);

畫面結構


  return (
    <div className="container mx-auto p-4 max-w-lg">
      <h1 className="text-2xl font-bold mb-4">Speech Synthesis</h1>
      <textarea
        className="w-full p-2 border rounded mb-4"
        rows={3}
        value={text}
        onChange={(e) => setText(e.target.value)}
      />

      <div className="mb-4">
        <label className="block mb-2">Voice:</label>
        <select
          className="w-full p-2 border rounded"
          onChange={(e) => updateVoice(Number(e.target.value))}
        >
          {synthRef.current.getVoices().map((voice, index) => (
            <option key={index} value={index}>
              {voice.name} ({voice.lang})
            </option>
          ))}
        </select>
      </div>

      <div className="mb-4">
        <label className="block mb-2">Rate:</label>
        <input
          type="range"
          min="0.5"
          max="2"
          step="0.1"
          defaultValue="1"
          onChange={(e) => updateRate(Number(e.target.value))}
          className="w-full"
        />
      </div>
      <div className="mb-4">
        <label className="block mb-2">Pitch:</label>
        <input
          type="range"
          min="0.5"
          max="2"
          step="0.1"
          defaultValue="1"
          onChange={(e) => updatePitch(Number(e.target.value))}
          className="w-full"
        />
      </div>

      <div className="flex justify-between">
        <button
          type="button"
          onClick={speak}
          className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
        >
          {synthRef.current.speaking ? "Pause" : "Speak"}
        </button>
        <button
          type="button"
          onClick={stop}
          className="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600"
        >
          Stop
        </button>
      </div>
    </div>
  );

DEMO

https://codesandbox.io/p/devbox/dpgtq9


上一篇
[Day22]_Follow-Along-Link-Highlighter
下一篇
[Day24]_Sticky-Nav
系列文
React30——用 React 探索 JavaScript30 的魅力30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言